<?php
/**
 * doit.class.php
 * 
 * DoitPHP核心类,并初始化框架的基本设置
 * @author tommy <streen003@gmail.com>
 * @copyright Copyright (c) 2010 Tommy Software Studio
 * @link http://www.doitphp.com
 * @license New BSD License.{@link http://www.opensource.org/licenses/bsd-license.php}
 * @version $Id: doit.class.php 1.1 2011-08-17 08:01:01Z tommy $
 * @package core
 * @since 1.0
 */

if (!defined('IN_DOIT')) {
	exit();
}

/**
 * 定义错误提示级别
 */
error_reporting(E_ALL^E_NOTICE);

/**
 * 定义DoitPHP框架文件所在路径
 */
if (!defined('DOIT_ROOT')) {
	define('DOIT_ROOT', dirname(__FILE__) . DIRECTORY_SEPARATOR);
}
/**
 * 设置程序开始执行时间.根据实际需要,自行开启,如开启去掉下面的//
 */
//define('DOIT_START_TIME',microtime(true));

/**
 +------------------------------------------------------------------------------------
 * 定义项目contrller, model, view, widget, config, extension目录的路径
 +------------------------------------------------------------------------------------
 */

/**
 * 项目controller目录的路径
 */
if (!defined('CONTROLLER_DIR')) {
	define('CONTROLLER_DIR', APP_ROOT . 'application/controllers' . DIRECTORY_SEPARATOR);	
}

/**
 * 项目model目录的路径
 */
if (!defined('MODEL_DIR')) {
	define('MODEL_DIR', APP_ROOT . 'application/models' . DIRECTORY_SEPARATOR);	
}

/**
 * 项目view目录的路径
 */
if (!defined('VIEW_DIR')) {
	define('VIEW_DIR', APP_ROOT . 'application/views' . DIRECTORY_SEPARATOR);
}

/**
 * 项目config目录的路径
 */
if (!defined('CONFIG_DIR')) {
	define('CONFIG_DIR', APP_ROOT . 'application/config' . DIRECTORY_SEPARATOR);
}

/**
 * 项目widget目录的路径
 */
if (!defined('WIDGET_DIR')) {
	define('WIDGET_DIR', APP_ROOT . 'application/widgets' . DIRECTORY_SEPARATOR);
}

/**
 * 项目extension目录的路径
 */
if (!defined('EXTENSION_DIR')) {
	define('EXTENSION_DIR', APP_ROOT . 'application/extensions' . DIRECTORY_SEPARATOR);
}


/**
 +------------------------------------------------------------------------------------
 * 定义项目的主要配置信息,包括DEBUG调试,重写规则,默认的controller,action,时区等设置
 + -----------------------------------------------------------------------------------
 */

/**
 * 设置是否开启调试模式.开启后,程序运行出现错误时,显示错误信息,便于程序调试.
 * 默认为关闭,如需开启,将下面的false改为true.
 */
if (!defined('DOIT_DEBUG')) {
	define('DOIT_DEBUG', false);	
}

/**
 * 设置URL的Rewrite功能是否开启,如开启后,需WEB服务器软件如:apache或nginx等,要开启Rewrite功能.
 * 默认为关闭,如需开启,只需将false换成true.
 */
if (!defined('DOIT_REWRITE')) {
	define('DOIT_REWRITE', false);
}

/**
 * 设置时区,默认时区为东八区(中国)时区.
 * 如需更改时区,将'Asia/ShangHai'设置你所需要的时区.
 */
if (!defined('DOIT_TIMEZONE')) {
	define('DOIT_TIMEZONE', 'Asia/ShangHai');
}

/**
 * 设置系统默认的controller名称,默认为:Index
 * 如需更改,将Index换成所需要的.
 * 注:为提高不同系统平台的兼容性,名称首字母要大写,其余小写.
 */
if (!defined('DEFAULT_CONTROLLER')) {
	define('DEFAULT_CONTROLLER', 'Index');
}

/**
 *设置 系统默认的action名称,默认为index
 *如需更改,将index换成所需名称.
 *注:名称要全部使用小写字母.
 */
if (!defined('DEFAULT_ACTION')) {
	define('DEFAULT_ACTION', 'index');
}

/**
 * 定义网址路由的分割符
 * 注：分割符不要与其它网址参数等数据相冲突
 */
if (!defined('URL_SEGEMENTATION')) {
	define('URL_SEGEMENTATION', '/');
}

/**
 * 定义路由网址的伪静态网址的后缀
 * 注：不要忘记了.(点)
 */
if (!defined('URL_SUFFIX')) {
	define('URL_SUFFIX', '.html');
}

/**
 * 定义自定义URL路由规则开关
 */
if (!defined('CUSTOM_URL_ROUTER')) {
	define('CUSTOM_URL_ROUTER', false);
}

/**
 * Doitphp框架核心全局控制类
 * 
 * 用于初始化程序运行及完成基本设置
 * @author tommy <streen003@gmail.com>
 * @version 1.0
 */
abstract class doit {

	/**
	 * 控制器(controller)
	 * 
	 * @var string
	 */
	public static $controller;
	
	/**
	 * 动作(action)
	 * 
	 * @var string
	 */
	public static $action;
	
	/**
	 * 对象注册表
	 * 
	 * @var array
	 */
	public static $_objects = array();
	
	/**
	 * 载入的文件名(用于PHP函数include所加载过的)
	 * 
	 * @var array
	 */
	public static $_inc_files = array();
	
	/**
	 * 分析URL信息
	 * 
	 * 通过对URL(网址)的分析,获取当前运行的controller和action,赋值给变量self::controller, 和self::action,
	 * 方便程序调用,同时将URL中的所含有的变量信息提取出来 ,写入$_GET全局超级变量数组中.
	 * 
	 * 注:这里的URL的有效部分是网址'?'之前的部分.'?'之后的部分不再被分析,因为'?'之后的URL部分完全属于$_GET正常调用的范畴.
	 * 这里的网址分析不支持功能强大的路由功能,只是将网址中的'/'分隔开,经过简单地程序处理提取有用数据.
	 * @access private
	 * @return boolean
	 */
	private static function parse_request() {
		
		//当项目开启Rewrite设置时
		if (DOIT_REWRITE === false) {
			$path_url_string = strlen($_SERVER['SCRIPT_NAME']) > strlen($_SERVER['REQUEST_URI']) ? $_SERVER['SCRIPT_NAME'] : $_SERVER['REQUEST_URI'];
			$path_url_string = str_replace($_SERVER['SCRIPT_NAME'], '', $path_url_string);
								
		} else {			
			$path_url_string = str_replace(str_replace('/index.php', '', $_SERVER['SCRIPT_NAME']), '', $_SERVER['REQUEST_URI']);
			//去掉伪静态网址后缀
			$path_url_string = str_replace(URL_SUFFIX, '', $path_url_string);
		}
					
		//如网址(URL)含有'?'(问号),则过滤掉问号(?)及其后面的所有字符串
		$pos = strpos($path_url_string, '?');
		if ($pos !== false) {			
			$path_url_string = substr($path_url_string, 0, $pos);
		}
		
		//当自定义URL路由功能开启时
		if (CUSTOM_URL_ROUTER === true) {
			$router_config_file = CONFIG_DIR . 'router.ini.php';
			if (is_file($router_config_file)) {
				//加载router的设置文件
				$router_array = require_once $router_config_file;
				//利用正则表达式将自定义的网址替换掉.替换为真实的网址
				if ($router_array && is_array($router_array)) {					
					foreach ($router_array as $router_key=>$router_value) {
						$router_key = str_replace(array(':any', ':num'), array('.+?', '[0-9]+'), $router_key);											
						if (preg_match('#' . $router_key . '#', $path_url_string)) {											
							$path_url_string = preg_replace('#' . $router_key . '#', $router_value, $path_url_string);							
							break;
						}
					}					
				}
			}
		}
		
		//将处理过后的有效URL进行分析,提取有用数据.
		$url_info_array = explode(URL_SEGEMENTATION, $path_url_string);
		
		//获取 controller名称
		$controller_name  = (isset($url_info_array[1]) && $url_info_array[1] == true) ? $url_info_array[1] : ((isset($_GET['controller']) && $_GET['controller'] == true) ? htmlspecialchars(trim($_GET['controller'])) : DEFAULT_CONTROLLER);						
		//获取 action名称
		$action_name  = (isset($url_info_array[2]) && $url_info_array[2] == true) ? $url_info_array[2] : ((isset($_GET['action']) && $_GET['action'] == true) ? htmlspecialchars(trim($_GET['action'])) : DEFAULT_ACTION);
		
		//分析controller及action名称
		self::$controller 	=  ucfirst(strtolower($controller_name));
		self::$action 		=  strtolower($action_name);
		
		//变量重组,将网址(URL)中的参数变量及其值赋值到$_GET全局超级变量数组中
		$total_num = sizeof($url_info_array);
		if ($total_num > 4) {			
			for ($i = 3; $i < $total_num; $i +=2) {					
				if (!$url_info_array[$i]) {
					continue;
				}				
				$_GET[$url_info_array[$i]] = $url_info_array[$i + 1];
			}
		}
				
		return true;
	}
	
	/**
	 * 项目运行函数
	 * 
	 * 供项目入口文件(index.php)所调用,用于启动框架程序运行
	 * @access public
	 * @return object
	 */
	public static function run() {
		
		//定义变量_app
		static $_app = array();
		
		//分析URL
		self::parse_request();
		
		$app_id = self::$controller . '_' . self::$action;
		
		if ($_app[$app_id] == null) {
						
			//通过实例化及调用所实例化对象的方法,来完成controller中action页面的加载
			$controller = self::$controller . 'Controller';
			$action     = self::$action . 'Action';
			
			//加载基本文件:Base,Controller基类
			self::load_file(DOIT_ROOT . 'core/Base.class.php');
			self::load_file(DOIT_ROOT . 'core/Controller.class.php');
			
			//加载当前要运行的controller文件
			if (is_file(CONTROLLER_DIR . $controller . '.class.php')) {											
				//当文件在controller根目录下存在时,直接加载.
				self::load_file(CONTROLLER_DIR . $controller . '.class.php');				
			} else {																	
				//从controller的名称里获取子目录名称,注:controller文件的命名中下划线'_'相当于目录的'/'.
				$pos = strpos($controller, '_');										
				if ($pos !== false) {
					//当$controller中含有'_'字符时
					$child_dir_name 	= strtolower(substr($controller, 0, $pos));
					$controller_file 	= CONTROLLER_DIR . $child_dir_name . '/' . $controller . '.class.php';			
					
					if (is_file($controller_file)) {
						//当子目录中所要加载的文件存在时	
						self::load_file($controller_file);		
					} else {							
						//当文件在子目录里没有找到时
						self::display_404_error();
					}						
				} else {						
					//当controller名称中不含有'_'字符串时
					self::display_404_error();
				}										
			}
			
			//创建一个页面控制对象
			$app_object = new $controller();
			
			if (method_exists($controller, $action)){				
				$_app[$app_id] = $app_object->$action();				
			} else {				
				//所调用方法在所实例化的对象中不存在时.
				self::display_404_error();
			}			
		}
				
		return $_app[$app_id];
	}
	
	/**
	 * 显示404错误提示
	 * 
	 * 当程序没有找到相关的页面信息时,或当前页面不存在.
	 * @access public
	 * @return void
	 */
	private function display_404_error() {				
				
		//判断自定义404页面文件是否存在,若不存在则加载默认404页面		
		is_file(VIEW_DIR . 'error/error404.html') ? self::load_file(VIEW_DIR . 'error/error404.html') : self::load_file(DOIT_ROOT . 'views/html/error404.html');		
		
		//既然提示404错误信息,程序继续执行下去也毫无意义,所以要终止(exit).
		exit();		
	}
	
	/**
	 * DoitPHP核心类引导数组
	 * 
	 * 用于DoitPHP核心文件auto_load()时,引导路径
	 */
	public static $core_class_array = array(
	'Model'     		=> 'core/Model.class.php',	
	'db_mysqli'			=> 'core/db/db_mysqli.class.php',
	'db_mysql'			=> 'core/db/db_mysql.class.php',
	'db_pdo'			=> 'core/db/db_pdo.class.php',
	'Log'       		=> 'core/Log.class.php',
	'Widget'     		=> 'core/Widget.class.php',	
	'View'				=> 'core/View.class.php',
	'Module'     		=> 'core/Module.class.php',		
	'script'			=> 'lib/script.class.php',
	'html'				=> 'lib/html.class.php',
	'pagelist'			=> 'lib/pagelist.class.php',
	'cookie'			=> 'lib/cookie.class.php',
	'session'			=> 'lib/session.class.php',
	'cache_file'		=> 'lib/cache/cache_file.class.php',
	'cache_db'			=> 'lib/cache/cache_db.class.php',
	'file_list'			=> 'lib/file_list.class.php',
	'image_lib'			=> 'lib/image_lib.class.php',
	'pincode'			=> 'lib/pincode.class.php',
	'check'				=> 'lib/check.class.php',
	'player'			=> 'lib/player.class.php',
	'file_upload'		=> 'lib/file_upload.class.php',
	'excel'				=> 'lib/excel.class.php',
	'curl'				=> 'lib/curl.class.php',
	'client'			=> 'lib/client.class.php',
	'zip'				=> 'lib/zip.class.php',
	'cache_memcache'	=> 'lib/cache/cache_memcache.class.php',
	'cache_xcache'		=> 'lib/cache/cache_xcache.class.php',
	'pinyin'			=> 'lib/pinyin.class.php',
	'router'			=> 'lib/router.class.php',
	'form'				=> 'lib/form.class.php',
	'cart'				=> 'lib/cart.class.php',	
	'xml'				=> 'lib/xml.class.php',
	'loader'			=> 'lib/loader.class.php',
	'wsdl'				=> 'lib/wsdl.class.php',
    'cache_apc'			=> 'lib/cache/cache_apc.class.php',
	'cache_eaccelerator'=> 'lib/cache/cache_eaccelerator.class.php',
	'db_sqlite'			=> 'core/db/db_sqlite.class.php',
	'db_oracle'			=> 'core/db/db_oracle.class.php',
	'db_postgres'		=> 'core/db/db_postgres.class.php',
	'db_mssql'			=> 'core/db/db_mssql.class.php',
	'db_mongo'			=> 'core/db/db_mongo.class.php'
	);
	
	/**
	 * 项目文件的自动加载
	 * 
	 * doitPHP系统自动加载核心类库文件(core目录内的文件)及运行所需的controller文件、model文件、widget文件等
	 * 
	 * 注:并非程序初始化时将所有的controller,model等文件都统统加载完,再执行其它.理解本函数前一定要先理解auto_load()的作用.
	 * 当程序运行时发现所需的文件没有找到时,auto_load()才会被激发,按照auto_load()的程序设计来完成对该文件的加载
	 * 
	 * @access public
	 * @param string $class_name 所需要加载的类的名称,注:不含后缀名
	 * @return void
	 */
	public static function auto_load($class_name) {
		
		//doitPHP核心类文件的加载分析	
		if (isset(self::$core_class_array[$class_name])) {			
			//当$class_name在核心类引导数组中存在时, 加载核心类文件			
			self::load_file(DOIT_ROOT . self::$core_class_array[$class_name]);			
		} else if (substr($class_name, -5) == 'Model') {			
			//modlel文件自动加载分析			
			if (is_file(MODEL_DIR . $class_name . '.class.php')) {				
				//当所要加载的model文件存在时
				self::load_file(MODEL_DIR . $class_name . '.class.php');
			} else {				
				//当所要加载的文件不存在时,显示错误提示信息
				Controller::halt('The Model file: ' . $class_name . ' is not exists!');	
			}			
		} else if(substr($class_name, -6) == 'Widget') {
			//加载所要运行的widget文件
			if (is_file(WIDGET_DIR . $class_name . '.class.php')) {
				//当所要加载的widget文件存在时
				self::load_file(WIDGET_DIR . $class_name . '.class.php');
			} else {
				Controller::halt('The Widget file: ' . $class_name . ' is not exists!');
			}
		} else {			
			//分析扩展目录文件
			if (is_file(EXTENSION_DIR . $class_name . '.class.php')) {
				//当扩展目录内文件存在时,则加载文件
				self::load_file(EXTENSION_DIR . $class_name . '.class.php');
			} else {
				//当所要加载的文件不存在时,提示错误信息
				Controller::halt('The File:' . $class_name .'.class.php is not exists!');
			}		
		}
	}
	
	/**
	 * 获取当前运行的controller名称
	 * 
	 * @example $controller_name = doit::get_controller_id();
	 * @access public
	 * @return string controller名称(字母全部小写)
	 */
	public static function get_controller_id() {
		
		return strtolower(self::$controller);
	}
	
	/**
	 * 获取当前运行的action名称
	 * 
	 * @example $action_name = doit::get_action_id();
	 * @access public
	 * @return string action名称(字母全部小写)
	 */
	public static function get_action_id() {
		
		return self::$action;
	}
	
	/**
	 * 返回唯一的实例(单例模式)
	 * 
	 * 程序开发中,model,module, widget, 或其它类在实例化的时候,将类名登记到doitPHP注册表数组($_objects)中,当程序再次实例化时,直接从注册表数组中返回所要的对象.
	 * 若在注册表数组中没有查询到相关的实例化对象,则进行实例化,并将所实例化的对象登记在注册表数组中.此功能等同于类的单例模式.
	 * 
	 * 注:本方法只支持实例化无须参数的类.如$object = new pagelist(); 不支持实例化含有参数的.
	 * 如:$object = new pgelist($total_list, $page);
	 *
	 * <code>
	 * $object = doit::singleton('pagelist');
	 * </code>
	 * 
	 * @access public
	 * @param string $class_name  要获取的对象的类名字
	 * @return object 返回对象实例
	 */
	public static function singleton($class_name) {
		
		//参数分析
		if (!$class_name) {
			return false;
		}
		
		$key = strtolower($class_name);
		
		if (isset(self::$_objects[$key])) {				
			return self::$_objects[$key];
		}
		
		return self::$_objects[$key] = new $class_name();
	}
	
	/**
	 * 静态加载文件(相当于PHP函数require_once)
	 * 
	 * include 以$file_name为名的php文件,如果加载了,这里将不再加载.
	 * @param string $file_name 文件路径,注:含后缀名	
	 * @return boolean 
	 */
	public static function load_file($file_name) {

		//参数分析
		if (!$file_name) {
			return false;
		}
		 
		//判断文件有没有加载过,加载过的直接返回true
		if (self::$_inc_files[$file_name] == false) {
			
			//分析文件是不是真实存在,若文件不存在,则只能...
			if (!is_file($file_name)) {
				//当所要加载的文件不存在时,错误提示
				Controller::halt('The file:' . $file_name . ' not found!');
			}
			
			include_once $file_name;
			self::$_inc_files[$file_name] = true;
		}
		
		return self::$_inc_files[$file_name];
	}
}

/**
 * 调用SPL扩展,注册__autoload()函数.
 */
spl_autoload_register(array('doit', 'auto_load'));